package impl.game.grid;

import framework.Person;
import framework.PersonsFactory;
import impl.game.grid.dnd.ComponentDND;

import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;

/**
 * Date: 08.04.2008
 * Time: 18:15:18
 */
public class QuadranglePanel extends JPanel implements ActionListener {
    private static final boolean VERBOSE = true;
    public Object AntiAlias = RenderingHints.VALUE_ANTIALIAS_ON;
    public Object Rendering = RenderingHints.VALUE_RENDER_SPEED;

    protected GridGame game;
    protected QuadrangleDrawPanel drawPanel;
    protected Button butStart, butStop, butTurn;
    protected JPanel panleft, panperson, panbutton;
    protected boolean isStart;

    protected final PersonTool[] persons;
    private JMenuItem runMI, stopMI, exitMI;
    private GridRenderer renderer;
    private final Runner runner;


    public class PersonTool extends ComponentDND {
        protected String className;

        public PersonTool(String name, String className, String shortName) {
            super(name, shortName);
            this.className = className;
        }

        protected String getMyClassName() {
            return className;
        }
    }

    public class QuadrangleDrawPanel extends JPanel {
        protected class MyTargetListener implements DropTargetListener {

            private boolean isDragOk(DropTargetDragEvent e) {
                return e.isDataFlavorSupported(DataFlavor.stringFlavor);
            }

            public void dragEnter(DropTargetDragEvent dtde) {
                if (!isDragOk(dtde)) {
                    dtde.rejectDrag();
                    return;
                }
                //TODO setActive(true);
                dtde.acceptDrag(dtde.getDropAction());
            }

            public void dragOver(DropTargetDragEvent dtde) {
                if (!isDragOk(dtde)) {
                    dtde.rejectDrag();
                    return;
                }
                dtde.acceptDrag(dtde.getDropAction());
                if (!dragPlayer.isInit()) {
                    Object data = null;
                    try {
                        data = dtde.getTransferable().getTransferData(DataFlavor.stringFlavor);
                        if (data == null)
                            throw new NullPointerException();
                    } catch (Throwable t) {
                        t.printStackTrace();
                    }
                    if (data instanceof String) {
                        //dragPlayer.setName(((String) data).charAt(((String) data).lastIndexOf(".") + 1) + "");                        
                        try {
                            Class klass = Class.forName((String) data);
                            Person person = (Person) klass.newInstance();
                            dragPlayer.setName(person.getShortName());
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InstantiationException e) {
                            e.printStackTrace();
                        }


                    }
                }
                dragPlayer.setLocation(dtde.getLocation());
                repaint();
            }

            public void dropActionChanged(DropTargetDragEvent dtde) {
                if (!isDragOk(dtde)) {
                    dtde.rejectDrag();
                    return;
                }
                dtde.acceptDrag(dtde.getDropAction());
            }

            public void dragExit(DropTargetEvent dte) {
                //TODO setActive(false);
                dragPlayer.setLocation(-10, -10);
                dragPlayer.setName("");
                repaint();
            }

            public void drop(DropTargetDropEvent dtde) {
                if (dtde.isDataFlavorSupported(DataFlavor.stringFlavor))
                    try {
                        dtde.acceptDrop(dtde.getDropAction());
                        Object data = dtde.getTransferable().getTransferData(DataFlavor.stringFlavor);
                        if (data instanceof String) {
                            //TODO setAction((String) data);
                            setPlayer(dtde.getLocation(), (String) data);
                            dtde.dropComplete(true);
                        } else {
                            dtde.dropComplete(false);
                        }
                    } catch (Throwable t) {
                        t.printStackTrace();
                        dtde.dropComplete(false);
                    } finally {
                        dragExit(dtde);
                    }
            }
        }

        protected int i = 0;
        protected float coord = 0;
        protected final int border = 2;
        public BufferedImage bimg;

        protected Player dragPlayer;

        private DropTarget dropTarget;
        private DropTargetListener dtListener;
        private int acceptableActions = DnDConstants.ACTION_COPY;

        public QuadrangleDrawPanel(LayoutManager layout) {
            super(layout);

            // TARGET
            dtListener = new MyTargetListener();
            dropTarget = new DropTarget(this,
                    this.acceptableActions,
                    this.dtListener,
                    true);

            dragPlayer = new Player(-10, -10, "");

        }

        protected void setPlayer(Point p, String className) {
            int i = renderer.getPlayerIndex(p);
            if (i >= 0) {
                try {
                    Class klass = Class.forName(className);
                    Person person = (Person) klass.newInstance();
                    game.setPerson(i, person);
                    renderer.setPlayerShortName(i, person.getShortName());
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                }
            }
        }

        private BufferedImage createBinaryImage(int w, int h) {
            return (BufferedImage) createImage(w, h);
        }

        public Graphics2D createGraphics2D(int width,
                                           int height,
                                           BufferedImage bi,
                                           Graphics g) {
            Graphics2D g2;
            if (bi != null) {
                g2 = bi.createGraphics();
            } else {
                g2 = (Graphics2D) g;
            }
            g2.setBackground(getBackground());
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, AntiAlias);
            g2.setRenderingHint(RenderingHints.KEY_RENDERING, Rendering);
            g2.clearRect(0, 0, width, height);
            return g2;
        }

        private int cnt = 0;
        private long t0 = System.currentTimeMillis();

        public void paint(Graphics g) {
            Dimension d = getSize();
            if (bimg == null) {
                bimg = createBinaryImage(d.width, d.height);
            }
            Graphics2D g2 = createGraphics2D(d.width, d.height, bimg, g);
            renderer.render(g2);
            dragPlayer.draw(g2);
            g.drawImage(bimg, 0, 0, null);
            g2.dispose();
            if (VERBOSE) {
                cnt++;
                if (cnt > 500) {
                    long t = System.currentTimeMillis();
                    System.out.println("FPS:" + (cnt * 1000 / (t - t0)));
                    t0 = t;
                    cnt = 0;
                }
            }
        }
    }


    private JMenuBar createMenuBar() {

        JPopupMenu.setDefaultLightWeightPopupEnabled(false);
        JMenuBar menuBar = new JMenuBar();

        JMenu file = menuBar.add(new JMenu("File"));
        exitMI = file.add(new JMenuItem("Exit"));
        exitMI.addActionListener(this);


        JMenu options = menuBar.add(new JMenu("Options"));

        runMI = options.add(new JMenuItem("Start"));
        runMI.addActionListener(this);

        stopMI = options.add(new JMenuItem("Stop"));
        stopMI.setEnabled(false);
        stopMI.addActionListener(this);

        return menuBar;
    }

    protected void start() {
        System.out.println("START");
        runMI.setEnabled(false);
        butStart.setEnabled(false);
        butTurn.setEnabled(false);

        runner.setPause(false);

        stopMI.setEnabled(true);
        butStop.setEnabled(true);
    }

    protected void stop() {
        System.out.println("STOP");
        stopMI.setEnabled(false);
        butStop.setEnabled(false);

        runner.setPause(true);

        runMI.setEnabled(true);
        butStart.setEnabled(true);
        butTurn.setEnabled(true);
    }

    protected void turn(int n) {
        System.out.println("TURN");
        runner.turn(n);
    }

    //todo Constructor must be a first
    public QuadranglePanel(GridGame game) throws HeadlessException {
        super(new BorderLayout());

        persons = new PersonTool[PersonsFactory.STRATEGIES_COUNT];
        for (int i = 0; i < persons.length; i++) {
            Person str = PersonsFactory.getPerson(i);
            persons[i] = new PersonTool(str.getClass().getSimpleName(), str.getClass().getName(), str.getShortName());
            //persons[i].setText("TEST " + i);
        }

        runner = new Runner();
        runner.setPriority(Thread.MIN_PRIORITY);
        runner.start();
        isStart = false;
        renderer = new GridRenderer(game, 50, 18);
//        renderer.setGame(game);
//        renderer.setCellSize(50, 18);

        add(createMenuBar(), BorderLayout.NORTH);

        this.game = game;
        drawPanel = new QuadrangleDrawPanel(new BorderLayout());


        butTurn = new Button("Turn");
        butTurn.addActionListener(
                new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        turn(1);
                    }
                }
        );
        butStart = new Button("Start");
        butStart.addActionListener(
                new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        start();
                    }
                }
        );
        butStop = new Button("Stop");
        butStop.addActionListener(
                new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        stop();
                    }
                }
        );
        butStop.setEnabled(false);

        panleft = new JPanel(new BorderLayout());
        panperson = new JPanel(new GridLayout(15, 1, 5, 5));
        panbutton = new JPanel();

        panbutton.add(butStart);
        panbutton.add(butStop);
        panbutton.add(butTurn);
        for (PersonTool person : persons) {
            panperson.add(person);
        }

        add(BorderLayout.CENTER, drawPanel);

        panleft.add(panbutton, BorderLayout.NORTH);
        panleft.add(panperson, BorderLayout.CENTER);

        add(BorderLayout.WEST, panleft);
    }

    /** Invoked when an action occurs. */
    public void actionPerformed(ActionEvent e) {
        if (e.getSource().equals(exitMI)) {
            System.exit(0);
        } else if (e.getSource().equals(runMI)) {
            start();
        } else if (e.getSource().equals(stopMI)) {
            stop();
        }

    }

    private class Runner extends Thread {
        private long sleepAmount = 0;
        private int turnToDo = 0;

        @Override
        public void run() {
            int x = 0;
            long t0 = System.currentTimeMillis();
            while (true) {
                try {
                    if (turnToDo != 0) {
                        game.turn(1);
                        renderer.updateScores();
                        drawPanel.repaint();
                        if (VERBOSE) {
                            x += 1;
                            if (x > 100000) {
                                long t = System.currentTimeMillis();
                                System.out.println("IPS:" + (x * 1000 / (t - t0)));
                                t0 = t;
                                x = 0;
                            }
                        }
                        if (turnToDo > 0)
                            turnToDo--;
                        sleep(sleepAmount);
                        yield();
                    } else {
                        x = 0;
                        t0 = System.currentTimeMillis();
                        sleep(100);
                    }
                } catch (InterruptedException e) {
                }
            }
        }

        public void setPause(boolean pause) {
            turnToDo = pause ? 0 : -1;
        }

        public void setSleepAmount(long sleepAmount) {
            this.sleepAmount = sleepAmount;
        }

        public void turn(int n) {
            turnToDo = n;
        }
    }
}

